package structure.timewheel;

import java.util.concurrent.DelayQueue;


public class TimeWheel {

    /**
     * 一个时间槽的时间
     * 就是这个精度
     */
    private final long tickMs;

    /**
     * 时间轮大小
     */
    private final int wheelSize=10;

    /**
     * 槽,
     * 这里只为了加TimedTask,
     * 取用 用delayQueue
     * 
     * buckets,delayQueue 是2个容器 指向同一堆Bucket
     * 
     * 元素有wheelSize 个Bucket
     * 
     * 随着时间推进, 重复使用, 这样就能在有限的wheelSize 存储无限的时间个
     */
    private final Bucket[] buckets;
    
    
    /**
     * 时间轮指针, 每层时间轮是同个时间, 但是这里值不同 就是tickMs 精度不同
     */
    private long currentTimestamp;

    /**
     * 上层时间轮
     */
    private volatile TimeWheel overflowWheel;


    TimeWheel(long tickMs,  long currentTimestamp) {
        this.currentTimestamp = currentTimestamp;
        this.tickMs = tickMs;
        this.buckets = new Bucket[wheelSize];
        this.currentTimestamp = currentTimestamp - (currentTimestamp % tickMs);

        for (int i = 0; i < wheelSize; i++) {
            buckets[i] = new Bucket();
        }
    }

    private TimeWheel getOverflowWheel() {
        if (overflowWheel == null) {
            synchronized (this) {
                if (overflowWheel == null) {
                    overflowWheel = new TimeWheel(tickMs * wheelSize, currentTimestamp);
                }
            }
        }
        return overflowWheel;
    }

    /**
     * 添加任务到某个时间轮
     * 失败,说明按照tickMs的精度,现在currentTimestamp已经到timedTask执行的时机了
     */
    public boolean addTask(TimedTask timedTask) {

        long expireTimestamp = timedTask.getExpireTimestamp();

        // 到期了 不用存了, 去干吧
        if (expireTimestamp - currentTimestamp < tickMs) {
            return false;
        }

        // 本时间轮 就可以处理
        if (expireTimestamp - currentTimestamp < tickMs * wheelSize) {
            //扔进当前时间轮的某个槽中，只有时间【大于某个槽】，才会放进

            int buckIndex = (int) ((expireTimestamp / tickMs) % wheelSize);
            Bucket bucket = buckets[buckIndex];
            bucket.addTask(timedTask);

            //bucket里面的到期时间 变化了
            boolean expireChange = bucket.setExpire(expireTimestamp - expireTimestamp % tickMs);
            if (expireChange) {
                //加入,等轮到调用
                Timer.delayQueue.offer(bucket);
            }
            return true;
        }

        //上层时间轮试试
        {
            // 当maybeInThisBucket大于等于wheelSize时，需要将它扔到上一层的时间轮
            getOverflowWheel().addTask(timedTask);
        }

        return true;
    }

    /**
     * 尝试推进一下指针
     */
    protected void advanceClock(long timestamp) {
        
        if (timestamp<(currentTimestamp + tickMs)){
            return;
        }
        
        //本时间轮要更新, 上层的时间轮都要
        currentTimestamp = timestamp - (timestamp % tickMs);

        if (overflowWheel != null) {
            getOverflowWheel().advanceClock(timestamp);
        }
    }
}
